#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <ctype.h>

#ifdef _WIN32
#define popen _popen
#define snprintf _snprintf
#endif

typedef struct _stub {
	char *name;
	char *origin;
	struct _stub *next;
} stub;

void usage( char *name ) {
	fprintf( stderr,
		"Usage: %s [-n nm] [-m make] libs...\n"
		"nm   -- The command used to run nm on liteos objects\n"
		"make -- The command used to build liteos\n\n"
		"libs are import libraries (.a files) typically from\n"
		"dk/lib/nkm and dk/lib/w32\n",
		name );
}

int main( int argc, char **argv ) {
	char line[1024];
	char *make = "make";
	char *nm = "nm";
	char *origin = "unknown.a";
	stub *functions = NULL, *new_f, *imports = NULL, *search;
	FILE *make_f, *nm_f;
	int i, libstart = argc;
	FILE *out = fopen("tests/stubs.tst","w");

	if( argc == 1 ) {
		if( out ) fclose( out );
		usage(argv[0]);
		return 1;
	}

	if( !out ) {
		fprintf( stderr, "Could not write file tests/stubs.tst\n" );
		return 1;
	}

	fprintf( out, "# Automatically generated by stubgen\n" );

	for( i = 1; i < argc; i++ ) {
		if( !strcmp( argv[i], "-m" ) ) {
			make = argv[i+1];
			i++;
		} else if( !strcmp( argv[i], "-n" ) ) {
			nm = argv[i+1];
			i++;
		} else { libstart = i; break; }
	}

	snprintf( line, sizeof(line), "%s test 2>&1", make );
	make_f = popen( line, "r" );

	if( !make_f ) {
		fclose( out );
		fprintf( stderr, "Could not run %s test\n", make );
		return 1;
	}

	while( fgets( line, sizeof(line), make_f ) ) {
		char *end_of_location;
		char *begin_q, *end_q;

		if( !strstr( line, "undefined reference to" ) ) continue;

		end_of_location = strrchr( line, ':' );

		if( !end_of_location ) continue;

		begin_q = strchr( end_of_location, '`' );
		end_q = strchr( end_of_location, '\'' );

		if( !begin_q || !end_q ) continue;

		begin_q += 2; /* skip `_ */

		memmove( line, begin_q, end_q - begin_q );
		line[end_q - begin_q] = 0;

		for( new_f = functions; new_f; new_f = new_f->next )
			if( !strcmp( new_f->name, line ) ) break;

		if( new_f ) continue;

		new_f = (stub *)malloc( sizeof(stub) );
		if( !new_f ) 
		{
			fprintf( stderr, "Out of memory\n" ); 
			fclose( out );
			pclose( make_f );
			return 1;
		}

		new_f->name = strdup( line );
		new_f->next = functions;
		functions = new_f;
	}

	/* Scan libraries and collect available import sections */
	for( i = libstart; i < argc; i++ ) {
		snprintf( line, sizeof(line), "%s %s", nm, argv[i] );
		nm_f = popen( line, "r" );

		for( origin = argv[i]; *argv[i]; argv[i]++ )
			if( *argv[i] == '/' || *argv[i] == '\\' )
				origin = argv[i] + 1;


		if( !nm_f ) {
			fprintf( stderr, "Could not run %s\n", line );
			continue;
		}

		while( fgets( line, sizeof(line), nm_f ) ) {
			char *import_sign, *eol;

			if( !(import_sign = strstr( line, " I " )) ) continue;

			import_sign += 3;
			while( *import_sign && isspace(*import_sign) ) import_sign++;

			/* Strip ws after name */
			for( eol = import_sign; *eol && !isspace(*eol); eol++ );

			*eol = 0;

			for( new_f = imports; new_f; new_f = new_f->next )
				if( !strcmp( new_f->name, import_sign ) ) break;

			if( new_f ) continue;

			new_f = (stub *)malloc( sizeof(stub) );
			if( !new_f ) 
			{
				fprintf( stderr, "Out of memory\n" ); 
				fclose( out );
				pclose( make_f );
				pclose( nm_f );
				return 1;
			}

			new_f->name   = strdup( import_sign + 1 );
			new_f->origin = origin;
			new_f->next   = imports;
			imports = new_f;
		}

		pclose( nm_f );
	}

	/* Now we have a list of unique functions and a list of imports,
	lookup each function and output the entry from the import list. */
	for( new_f = functions; new_f; new_f = new_f->next ) {
		for( search = imports; search; search = search->next ) {
			if( !strcmp( new_f->name, search->name ) ) {
				fprintf( out, "%s %s\n", search->origin, search->name );
				continue;
			}
		}
	}

	fclose( out );
	pclose( make_f );
	return 0;
}
